package com.riguz.forks.framework.mvc.impl;

import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.http.HttpServletRequest;

import com.riguz.forks.framework.aop.ActionProxy;
import com.riguz.forks.framework.mvc.HandlerInvoker;
import com.riguz.forks.framework.mvc.Result;
import com.riguz.forks.framework.route.RouteHandler;
import com.riguz.gags.base.Classes;
import com.riguz.gags.base.Conversions;

import net.sf.cglib.proxy.Enhancer;

public class DefaultHandlerInvoker implements HandlerInvoker{
    private static final ConcurrentMap<Class<?>, Object> enhancedBeanCache = new ConcurrentHashMap<>();

    @Override
    public Result invoke(HttpServletRequest request, RouteHandler handler) throws Exception {
        Class<?> controllerClass = handler.getHandler().getActionClass();
        Method action = handler.getHandler().getActionMethod();

        Object controller = enhanceController(controllerClass);
        List<String> params = handler.getParameters();

        return (Result)Classes.invokeMethod(action, controller, this.buildArguments(action, params));
    }

    public Object[] buildArguments(Method method, List<String> strs) throws Exception{
        Class<?>[] paramTypes = method.getParameterTypes();
        if(paramTypes == null)
            return null;
        Object [] args = new Object[paramTypes.length];
        for(int i = 0; i < paramTypes.length; i++){
            args[i] = Conversions.parse(paramTypes[i], strs.get(i));
        }
        return args;
    }

    @SuppressWarnings("unchecked")
    private Object enhanceController(Class<?> cls){
        if (enhancedBeanCache.containsKey(cls)) {
            return enhancedBeanCache.get(cls);
        }

        ActionProxy proxy = new ActionProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(proxy);
        Object instance = enhancer.create(); 
        if (instance != null) {
            Object tmp = enhancedBeanCache.putIfAbsent(cls, instance);
            if(tmp != null)
                instance = tmp;
        }

        return instance;
    }
}
